%code top{
    #include <iostream>
    #include <assert.h>
    #include "parser.h"
    #include<string>
    #include<regex>
    #include<vector>
    extern Ast ast;
    int yylex();
    int yyerror( char const * );
    void stringsplit(const std::string& str, const std::string& split, std::vector<std::string>& res);
}

%code requires {
    #include "Ast.h"
    #include "SymbolTable.h"
    #include "Type.h"
}

%union {
    int itype;
    char* strtype;
    StmtNode* stmttype;
    ExprNode* exprtype;
    Type* type;
}

%start Program
%token <strtype> ID COM IDLIST FUNCPARALIST
%token <itype> INTEGER
%token IF ELSE 
%token WHILE
%token INT VOID
%token LPAREN RPAREN LBRACE RBRACE SEMICOLON COMMA
%token MUL DIV MOD
%token ADD SUB OR AND LESS ASSIGN MORE ME LE EQ UE
%token RETURN
%token CONST



%nterm <stmttype> Stmts Stmt AssignStmt BlockStmt IfStmt ReturnStmt DeclStmt FuncDef WhileStmt Parameter ParamList CommentStmt NonAssignStmt IDRefList
%nterm <exprtype> Exp AddExp Cond LOrExp PrimaryExp LVal RelExp LAndExp MulExp FuncRef 
%nterm <type> Type

%precedence THEN
%precedence ELSE
%%
Program
    : Stmts {
        ast.setRoot($1);
    }
    ;
Stmts
    : Stmt {$$=$1;}
    | Stmts Stmt{
        $$ = new SeqNode($1, $2);
    }
    ;
Stmt
    : AssignStmt {$$=$1;}
    | NonAssignStmt{$$=$1;}
    | BlockStmt {$$=$1;}
    | IfStmt {$$=$1;}
    | ReturnStmt {$$=$1;}
    | DeclStmt {$$=$1;}
    | FuncDef {$$=$1;}
    | WhileStmt {$$=$1;}
    | CommentStmt {$$=$1;}
    ;
LVal
    : ID {
        SymbolEntry *se;
        se = identifiers->lookup($1);
        if(se == nullptr)
        {
            fprintf(stderr, "identifier \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        $$ = new Id(se);
        delete []$1;
    }
    ;
CommentStmt
    : COM {
        $$ =new CommentStmt($1);
    }
    ;
AssignStmt
    :
    LVal ASSIGN Exp SEMICOLON {
        $$ = new AssignStmt($1, $3);
    }
    ;
NonAssignStmt
    :
    Exp SEMICOLON{
        $$ = new NonAssignStmt($1);
    }
    |
    SEMICOLON{
        $$ = new NonAssignStmt();
    }
    ;
BlockStmt
    :   LBRACE 
        {identifiers = new SymbolTable(identifiers);} 
        Stmts RBRACE 
        {
            $$ = new CompoundStmt($3);
            SymbolTable *top = identifiers;
            identifiers = identifiers->getPrev();
            delete top;
        }
    ;
IfStmt
    : IF LPAREN Cond RPAREN Stmt %prec THEN {
        $$ = new IfStmt($3, $5);
    }
    | IF LPAREN Cond RPAREN Stmt ELSE Stmt {
        $$ = new IfElseStmt($3, $5, $7);
    }
    ;
WhileStmt
    : WHILE LPAREN Cond RPAREN Stmt {
        $$ = new WhileStmt($3, $5);
    }
    ;
ReturnStmt
    :
    RETURN Exp SEMICOLON{
        $$ = new ReturnStmt($2);
    }
    ;
Exp
    :
    AddExp {$$ = $1;}
    ;
Cond
    :
    LOrExp {$$ = $1;}
    ;
PrimaryExp
    :
    LVal {
        $$ = $1;
    }
    | INTEGER {
        SymbolEntry *se = new ConstantSymbolEntry(TypeSystem::intType, $1);
        $$ = new Constant(se);
    }
    | LPAREN AddExp RPAREN {
        $$ = $2;
    }
    | FuncRef {
        $$ = $1;
    }
    | SUB PrimaryExp {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::UMINUS, nullptr, $2);
    }
    | ADD PrimaryExp {
        $$ = $2;
    }
    ;
MulExp
    :PrimaryExp {$$ = $1;}
    |
    MulExp MUL PrimaryExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MUL, $1, $3);
    }
    |
    MulExp DIV PrimaryExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::DIV, $1, $3);
    }
    |
    MulExp MOD PrimaryExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MOD, $1, $3);
    }
    ;
AddExp
    :
    MulExp {$$ = $1;}
    |
    AddExp ADD MulExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::ADD, $1, $3);
    }
    |
    AddExp SUB MulExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::SUB, $1, $3);
    }

    ;
RelExp
    :
    AddExp {$$ = $1;}
    |
    RelExp LESS AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::LESS, $1, $3);
    }
    |
    RelExp MORE AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::MORE, $1, $3);
    }
    |
    RelExp LE AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::LE, $1, $3);
    }
    |
    RelExp ME AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::ME, $1, $3);
    }
    |
    RelExp EQ AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::EQ, $1, $3);
    }
    |
    RelExp UE AddExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::UE, $1, $3);
    }
    ;
LAndExp
    :
    RelExp {$$ = $1;}
    |
    LAndExp AND RelExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::AND, $1, $3);
    }
    ;
LOrExp
    :
    LAndExp {$$ = $1;}
    |
    LOrExp OR LAndExp
    {
        SymbolEntry *se = new TemporarySymbolEntry(TypeSystem::intType, SymbolTable::getLabel());
        $$ = new BinaryExpr(se, BinaryExpr::OR, $1, $3);
    }
    ;
Type
    : INT {
        $$ = TypeSystem::intType;
    }
    | VOID {
        $$ = TypeSystem::voidType;
    }
    ;
DeclStmt
    :
    Type ID SEMICOLON {
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($1, $2, identifiers->getLevel());
        identifiers->install($2, se);
        $$ = new DeclStmt(new Id(se));
        delete []$2;
    }
    |
    CONST Type ID SEMICOLON {
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($2, $3, identifiers->getLevel());
        identifiers->install($3, se);
        $$ = new DeclStmt(new Id(se));
        delete []$3;
    }
    |
    Type ID ASSIGN Exp SEMICOLON{
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($1, $2, identifiers->getLevel());
        identifiers->install($2, se);
        delete []$2;
        $$ = new DeclAssignStmt(new Id(se), $4);

    }
    |
    CONST Type ID ASSIGN Exp SEMICOLON{
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($2, $3, identifiers->getLevel());
        identifiers->install($3, se);
        delete []$3;
        $$ = new DeclAssignStmt(new Id(se), $5);
    }
    |
    Type IDLIST SEMICOLON {
        std::vector<std::string> sv;
        std::string str=$2;
        stringsplit(str,",",sv);//[ |\t]*,[ |\t]*|[ |\t]+
        for(auto s :sv){
            if(s.length()==0)continue;
            std::vector<std::string> su;
            int state=0;
            stringsplit(s,"[ |\t]+|=",su);
            for(auto u : su){
                if(u.length()==0)continue;
                if(state == 0){
                    SymbolEntry *se;
                    se = new IdentifierSymbolEntry($1, u, identifiers->getLevel());
                    identifiers->install(u, se);
                    state++;
                }
                
            }
            
        }
        $$ = new DeclIdListStmt(str,$1);
        delete []$2;
    }
    |
    CONST Type IDLIST SEMICOLON {
        std::vector<std::string> sv;
        std::string str=$3;
        stringsplit(str,",",sv);//[ |\t]*,[ |\t]*|[ |\t]+
        for(auto s :sv){
            if(s.length()==0)continue;
            std::vector<std::string> su;
            int state=0;
            stringsplit(s,"[ |\t]+|=",su);
            for(auto u : su){
                if(u.length()==0)continue;
                if(state == 0){
                    SymbolEntry *se;
                    se = new IdentifierSymbolEntry($2, u, identifiers->getLevel());
                    identifiers->install(u, se);
                    state++;
                }
                
            }
            
        }
        $$ = new DeclIdListStmt(str,$2);
        delete []$3;
    }
    ;
ParamList
    :
    Parameter {$$=$1;}
    |
    FUNCPARALIST {
        std::vector<std::string> sv;
        std::string str=$1;
        stringsplit(str,",",sv);
        for(auto s :sv){
            if(s.length()==0)continue;
            std::vector<std::string> su;
            Type* type;
            int state=0;
            stringsplit(s,"[ |\t]+",su);
            for(auto u : su){
                if(u.length()==0)continue;
                if(state==0){
                    if(u=="int")type=TypeSystem::intType;
                    if(u=="void")type=TypeSystem::voidType;
                    state++;
                }else if(state==1){
                    SymbolEntry *se;
                    se = new IdentifierSymbolEntry(type, u, identifiers->getLevel());
                    identifiers->install(u, se);
                }
            }
        }
        $$ = new FuncParaList(nullptr,str);
        delete []$1;
    }
    ;
Parameter
    :
    Type ID {
        SymbolEntry *se;
        se = new IdentifierSymbolEntry($1, $2, identifiers->getLevel());
        identifiers->install($2, se);
        $$ = new FuncParaList(new Id(se),"");
        delete []$2;
    }
    ;
FuncDef
    :
    Type ID LPAREN ParamList RPAREN {
        Type *funcType;
        funcType = new FunctionType($1,{});
        SymbolEntry *se = new IdentifierSymbolEntry(funcType, $2, identifiers->getLevel());
        identifiers->install($2, se);
        identifiers = new SymbolTable(identifiers);
    }

    BlockStmt
    {
        SymbolEntry *se;
        se = identifiers->lookup($2);
        assert(se != nullptr);
        $$ = new FunctionDef(se, $4 , $7 );
        SymbolTable *top = identifiers;
        identifiers = identifiers->getPrev();
        delete top;
        delete []$2;
    }
    |
    Type ID LPAREN RPAREN{
        Type *funcType;
        funcType = new FunctionType($1,{});
        SymbolEntry *se = new IdentifierSymbolEntry(funcType, $2, identifiers->getLevel());
        identifiers->install($2, se);
        identifiers = new SymbolTable(identifiers);
    }
    
    BlockStmt
    {
        SymbolEntry *se;
        se = identifiers->lookup($2);
        assert(se != nullptr);
        $$ = new FunctionDef(se, $6);
        SymbolTable *top = identifiers;
        identifiers = identifiers->getPrev();
        delete top;
        delete []$2;
    }
    ;
IDRefList
    :
    Exp
    {
        $$ = new IdRefList($1,"");
    }
    |
    IDLIST
    {
        std::vector<std::string> sv;
        std::string str=$1;
        stringsplit(str,",",sv);
        for(auto s :sv){
            if(s.length()==0)continue;
            std::vector<std::string> su;
            stringsplit(s,"[ |\t]+",su);
            for(auto u : su){
                if(u.length()==0)continue;
                char firstc=u.at(0);
                if(firstc>='0'&&firstc<='9')continue;
                SymbolEntry *se;
                se = identifiers->lookup(u);
                if(se == nullptr)
                {
                    fprintf(stderr, "identifier \"%s\" is undefined\n", (char*)$1);
                    delete [](char*)$1;
                    assert(se != nullptr);
                }
            }
        }
        $$ = new IdRefList(nullptr,str);
        delete []$1;
    }
    ;

FuncRef 
    :
    ID LPAREN RPAREN{
        SymbolEntry *se;
        se = identifiers->lookup($1);
        if(se == nullptr)
        {
            fprintf(stderr, "identifier \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        $$ = new FunctionRef(se);
        delete []$1;
    }
    |
    ID LPAREN IDRefList RPAREN{
        SymbolEntry *se;
        se = identifiers->lookup($1);
        if(se == nullptr)
        {
            fprintf(stderr, "identifier \"%s\" is undefined\n", (char*)$1);
            delete [](char*)$1;
            assert(se != nullptr);
        }
        $$ = new FunctionRef(se ,$3);
        delete []$1;
    }
    ;
%%
void stringsplit(const std::string& str, const std::string& split, std::vector<std::string>& res)
{
	//std::regex ws_re("\\s+"); // 正则表达式,匹配空格 
	std::regex reg(split);		// 匹配split
	std::sregex_token_iterator pos(str.begin(), str.end(), reg, -1);
	decltype(pos) end;              // 自动推导类型 
	for (; pos != end; ++pos)
	{
		res.push_back(pos->str());
	}
}


int yyerror(char const* message)
{
    std::cerr<<message<<std::endl;
    return -1;
}
